import type { MapScene } from "./mapScene";
import type { MapLayer } from "./mapLayer";

/**
 * 地图上下文
 */
export class MapContext<S extends MapScene = MapScene, L extends MapLayer = MapLayer> {
    /** 场景实例 */
    protected scene?: S = undefined;
    /** 图层集合 */
    protected layers: Record<string, L> = {};
    /** zIndex 排序 */
    protected zIndexSort: L[] = [];

    constructor() {}

    /*------------------------------------------------------*/
    /* 基本方法 */

    /**
     * 销毁地图上下文
     */
    async destroy(): Promise<this> {
        this.scene = undefined;
        await this.removeAllLayersFromScene();
        Object.keys(this.layers).forEach((key) => {
            delete this.layers[key];
        });
        this.removeMapScene();
        return this;
    }

    /*------------------------------------------------------*/
    /* scene 方法 */

    /**
     * 添加场景
     * @param mapScene 场景实例
     * @param id       场景id
     * @returns        地图上下文实例
     */
    async addMapScene(mapScene: S): Promise<MapContext> {
        if (this.scene) {
            throw new Error(`Scene already exists`);
        }
        await mapScene.init();
        this.scene = mapScene;
        return this;
    }
    /**
     * 删除场景
     * @returns 地图上下文实例
     */
    removeMapScene(): MapContext {
        const scene = this.tryGetMapScene();
        if (scene) {
            scene.destroyScene();
        }
        this.scene = undefined;
        return this;
    }
    /**
     * 获取场景 获取不到时抛出异常
     * @returns 场景实例
     */
    getMapScene(): MapScene {
        const scene = this.tryGetMapScene();
        if (!scene) {
            throw new Error(`Scene does not exist`);
        }
        return scene;
    }
    /**
     * 尝试获取场景 获取不到时返回 undefined
     * @returns 场景实例
     */
    tryGetMapScene(): MapScene | undefined {
        return this.scene;
    }

    /*------------------------------------------------------*/
    /* 图层方法 */

    /**
     * 添加图层
     * @param mapLayer 图层实例
     * @param creator  图层创建器
     * @returns        地图上下文实例
     */
    addMapLayer(mapLayer: L): MapContext {
        const id = mapLayer.id;
        if (this.tryGetMapLayer(id)) {
            throw new Error(`Layer with id ${id} already exists`);
        }
        this.addLayerToIndexSort(mapLayer);
        this.layers[id] = mapLayer;
        return this;
    }
    /**
     * 删除图层
     * @param mapLayer 图层实例或 id
     * @returns 地图上下文实例
     */
    async removeMapLayer(idOrInstance: string | L): Promise<MapContext> {
        const layer = this.getMapLayer(idOrInstance);
        await this.removeLayerFromScene(layer);
        this.removeLayerFromIndexSort(layer);
        delete this.layers[layer.id];
        return this;
    }
    /**
     * 获取图层 获取不到时抛出异常
     * @param id 图层 id
     * @returns 图层实例
     */
    getMapLayer(idOrInstance: string | L): L {
        const layer = this.tryGetMapLayer(idOrInstance);
        if (!layer) {
            throw new Error(`Layer with id ${idOrInstance} does not exist`);
        }
        return layer;
    }
    /**
     * 尝试获取图层 获取不到时返回 null
     * @param id 图层 id
     * @returns 图层实例
     */
    tryGetMapLayer(idOrInstance: string | MapLayer): Nullable<L> {
        if (typeof idOrInstance === "string") {
            return this.layers[idOrInstance] ?? null;
        } else {
            return this.layers[idOrInstance.id] ?? null;
        }
    }
    /**
     * 获取所有图层
     * @returns 所有图层
     */
    getMapLayers(): L[] {
        return Object.values(this.layers);
    }
    /**
     * 交换图层 zIndex
     * @param layer1 图层1实例或 id
     * @param layer2 图层2实例或 id
     * @returns      地图上下文实例
     */
    swapLayerIndex(layer1: L, layer2: L): MapContext {
        const mapLayer1 = this.getMapLayer(layer1);
        const mapLayer2 = this.getMapLayer(layer2);
        if (!mapLayer1 || !mapLayer2 || mapLayer1 === mapLayer2) return this;
        const zIndex1 = mapLayer1.getLayerIndex();
        const zIndex2 = mapLayer2.getLayerIndex();
        mapLayer1.setLayerIndex(zIndex2);
        mapLayer2.setLayerIndex(zIndex1);
        return this;
    }
    /**
     * 设置 layer 的 zIndex
     */
    setLayerIndex(mapLayer: L, zIndex: number): MapContext {
        // 1. 如果图层已存在，且 zIndex 未变则直接返回
        const existingLayer = this.zIndexSort.find((layer) => layer === mapLayer);
        if (existingLayer) {
            const oldZIndex = this.getMapLayer(mapLayer).getLayerIndex();
            if (oldZIndex === zIndex) return this;
            this.removeLayerFromIndexSort(mapLayer);
        }

        // 2. 遍历 zIndexSort 寻找合适的位置
        let layerIndex = zIndex;
        let layer = mapLayer;

        for (let i = 0; i < this.zIndexSort.length; i++) {
            const itemLayer = this.zIndexSort[i];
            const itemIndex = itemLayer.getLayerIndex();

            if (itemIndex < layerIndex) {
                continue;
            }
            // 如果找到比目标 zIndex 大的项，插入新图层
            if (itemIndex > layerIndex) {
                this.zIndexSort.splice(i, 0, layer);
                return this;
            }
            // 如果已有相同 zIndex，替换，并递增 zIndex
            if (itemIndex === layerIndex) {
                this.zIndexSort[i] = layer;
                layer.setLayerIndex(layerIndex);
                layerIndex++; // 更新 zIndex
                layer = itemLayer; // 继续处理下一个图层
            }
        }

        // 3. 如果遍历结束且仍有图层未处理，添加到最后
        this.zIndexSort.push(layer);
        layer.setLayerIndex(layerIndex);

        return this;
    }
    /**
     * 重排 zIndex 排序
     */
    protected reSortIndexSort(): MapContext {
        this.zIndexSort.sort((a, b) => {
            const aIndex = a.getLayerIndex();
            const bIndex = b.getLayerIndex();
            if (aIndex < bIndex) {
                return -1;
            }
            if (aIndex > bIndex) {
                return 1;
            }
            return 0;
        });
        return this;
    }
    /**
     * 添加 layer 到 zIndex 排序
     * @param mapLayer 图层实例
     */
    protected addLayerToIndexSort(mapLayer: L): MapContext {
        if (this.zIndexSort.length < 0) {
            this.zIndexSort.push(mapLayer);
        }
        for (let i = 0; i < this.zIndexSort.length; i++) {
            const layerIndex = mapLayer.getLayerIndex();
            const item = this.zIndexSort[i];
            const itemIndex = item.getLayerIndex();
            if (itemIndex < layerIndex) {
                continue;
            }
            if (itemIndex === layerIndex) {
                mapLayer.setLayerIndex(itemIndex + 1);
                continue;
            }
            this.zIndexSort.splice(i, 0, mapLayer);
        }
        return this;
    }
    /**
     * 从 zIndex 排序移除图层
     */
    protected removeLayerFromIndexSort(mapLayer: L): MapContext {
        const index = this.zIndexSort.indexOf(mapLayer);
        if (index >= 0) {
            this.zIndexSort.splice(index, 1);
        }
        return this;
    }
    /**
     * 从 zIndex 排序移除所有图层
     */
    protected removeAllLayersFromIndexSort(): MapContext {
        this.zIndexSort = [];
        return this;
    }

    /*------------------------------------------------------*/
    /* 场景图层方法 */

    /**
     * 向场景添加图层，未添加则添加，未初始化则初始化
     *
     * @param layer 图层实例
     * @returns     地图上下文实例
     */
    async addLayerToScene(mapLayer: L): Promise<MapContext> {
        if (!this.tryGetMapLayer(mapLayer)) {
            this.addMapLayer(mapLayer);
        }
        const scene = (await this.getMapScene().init()).getInstance();
        const instances = await mapLayer.init();

        const layer = mapLayer.getLayerInstance();
        const layerPopup = instances.layerPopup;
        const markerLayer = instances.markerLayer;

        if (scene.getLayer(layer.id)) {
            throw new Error(`Layer with id ${layer.id} already exists in scene`);
        } else {
            scene.addLayer(layer);
        }
        if (layerPopup) {
            scene.addPopup(layerPopup);
        }
        if (markerLayer) {
            scene.addMarkerLayer(markerLayer);
        }

        this.setLayerIndex(mapLayer, mapLayer.getLayerIndex());

        return this;
    }
    /**
     * 批量向指定场景添加图层，未添加则添加，未初始化则初始化
     *
     * @param layers 图层实例数组
     */
    async addLayersToScene(layers: L[]): Promise<MapContext> {
        const promises = layers.map((layer) => {
            return this.addLayerToScene(layer);
        });
        await Promise.all(promises);
        return this;
    }
    /**
     * 从场景移除图层
     * @param layer 图层实例
     * @returns     地图上下文实例
     */
    async removeLayerFromScene(mapLayer: L): Promise<MapContext> {
        const scene = this.getMapScene().getInstance();
        const layerNullable = this.tryGetMapLayer(mapLayer);
        if (!layerNullable) return this;
        const instances = layerNullable.getInstances();

        const layer = mapLayer.getLayerInstance();
        const layerPopup = instances.layerPopup;
        const markerLayer = instances.markerLayer;

        await scene.removeLayer(layer);
        if (layerPopup) {
            scene.removePopup(layerPopup);
        }
        if (markerLayer) {
            scene.removeMarkerLayer(markerLayer);
        }

        this.removeLayerFromIndexSort(mapLayer);
        return this;
    }
    /**
     * 移除所有图层
     * @returns 地图上下文实例
     */
    async removeAllLayersFromScene(): Promise<MapContext> {
        const layers = this.getMapLayers();
        const promises = layers.map((layer) => {
            return this.removeLayerFromScene(layer);
        });
        await Promise.all(promises);
        this.removeAllLayersFromIndexSort();
        return this;
    }
}
